#pragma once

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <functional>

namespace zxf
{

    typedef std::function<void(int &, struct sockaddr_in &, std::string)> func;
    enum myerr
    {
        USAGE_ERR = 1,
        SOCKET_ERR,
        BIND_ERR,
        RECVFROM_ERR,
        SENDTO_ERR,
        INITDICT_ERR,
        POPEN_ERR
    };

    static const std::string default_ip = "0.0.0.0";
    // 默认ip,一般服务器都绑定的0.0.0.0
    // 可以接受机器上任何以port端口的消息
    class server
    {
    public:
        server(uint16_t port, std::string ip = default_ip) // 直接使用 0.0.0.0 即可
            : _port(port), _ip(ip)
        {
        }

        // 初始化server
        void initserer()
        {
            _serverfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_serverfd == -1)
            {
                std::cout << "socket error:" << errno << strerror(errno) << std::endl;
                exit(SOCKET_ERR); // socket创建失败
            }
            std::cout << "socket success" << std::endl;
            // 绑定IP和port
            struct sockaddr_in s_addr;
            bzero(&s_addr, sizeof(s_addr)); // 内存初始化为0
            s_addr.sin_family = AF_INET;
            s_addr.sin_port = htons(_port);                  // 主机转网络
            s_addr.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1,点分十进制转换为int,2.主机转网络
            int t = bind(_serverfd, (struct sockaddr *)&s_addr, sizeof(s_addr));
            if (t == -1)
            {
                std::cout << "bind error:" << errno << strerror(errno) << std::endl;
                exit(BIND_ERR);
            }
            std::cout << "bind success" << std::endl;
        }
        // 启动服务器
        void runserver(func callback)
        {
            // 服务器是一个死循环
            char buf[1024];
            for (;;)
            {
                // 读取数据
                struct sockaddr_in c_addr;
                socklen_t len = sizeof(c_addr);
                ssize_t count = recvfrom(_serverfd, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&c_addr, &len);
                // 0 代表阻塞式读取
                if (count == -1)
                {
                    std::cout << "recvfrom error:" << errno << strerror(errno) << std::endl;
                    exit(RECVFROM_ERR);
                }
                // 现在我们把读取上来的信息当作 字符串使用
                buf[count] = 0;
                std::string message = "";
                message = buf;

                // 数据接受完成,开始处理信息

                std::string ip = inet_ntoa(c_addr.sin_addr); // 网络序列转为主机序列. 2,int->string
                uint16_t port = ntohs(c_addr.sin_port);
                std::cout << "[" << ip << std::to_string(port) << "]: " << message << std::endl; // 不是收到数据打印就完了,还有很多工作

                callback(_serverfd, c_addr, message);
            }
        }

        ~server()
        {
            close(_serverfd); // 关闭socket
        }

    private:
        // 创建服务器要绑定端口 _port ,_ip,文件fd
        std::string _ip; // 服务器绑定的ip
        uint16_t _port;  // 服务器绑定的端口号
        int _serverfd;   // 创建套接字需要的文件描述符
    };

} // namespace zxf
